﻿using System.Text;
using HslCommunication;
using HslCommunication.Core;
using HslCommunication.Profinet.Melsec;
using HslCommunication.Profinet.Omron;
using PLC.Domain.Enum;
using PLC.Domain.Models;
using PLC.Domain.Service.IRepository;

namespace PLC.Infrastructure.Repository;

public class PLCRepository:IPLCRepository
{
    /// <summary>
    /// 获取plc客户端
    /// </summary>
    /// <param name="plcConfig"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public IReadWriteNet GetPlcClient(PLCConfig plcConfig)
    {
        var ip = plcConfig.DeviceConfig.Ip;
        var port = plcConfig.DeviceConfig.Port;
        // 接收响应的过期时间
        var timeOut = 500;
        ArgumentException.ThrowIfNullOrEmpty(ip);
        IReadWriteNet readWriteNet;
        switch (plcConfig.DeviceConfig.PlcManufacturer)
        {
            case (int)EnumPlcManufacturer.三菱:
                if (plcConfig.DeviceConfig.Protocol==(int)EnumProtocol.三菱MCBinary)
                {
                    var mc = new MelsecMcNet(ip, port);
                    // 开启长连接
                    mc.ReceiveTimeOut = timeOut;
                    mc.ConnectTimeOut = timeOut;
                    mc.SetPersistentConnection();
                    readWriteNet = mc;
                }else if (plcConfig.DeviceConfig.Protocol==(int)EnumProtocol.三菱MCASCII)
                {
                    var mc = new MelsecMcAsciiNet(ip, port);
                    mc.ReceiveTimeOut = timeOut;
                    mc.ConnectTimeOut = timeOut;
                    mc.SetPersistentConnection();
                    readWriteNet = mc;
                }
                else
                {
                    throw new Exception("没有对应的plc协议的编码！");
                }
                break;
            case (int)EnumPlcManufacturer.欧姆龙:
                if (plcConfig.DeviceConfig.Protocol==(int)EnumProtocol.欧姆龙FinsUdp)
                {
                    var omron = new OmronFinsUdp(ip, port);
                    omron.ReceiveTimeout = timeOut;
                    readWriteNet = omron;
                }else if (plcConfig.DeviceConfig.Protocol==(int)EnumProtocol.欧姆龙EtherNet)
                {
                    var omron = new OmronCipNet(ip, port);
                    omron.Slot = 0;
                    omron.ReceiveTimeOut = timeOut;
                    omron.ConnectTimeOut = timeOut;
                    omron.SetPersistentConnection();
                    readWriteNet = omron;
                }
                else
                {
                    throw new Exception("没有对应的plc协议的编码！");
                }
                break;
            default: throw new Exception("没有对应枚举的客户端被创建");
        }
        return readWriteNet;
    }

    /// <summary>
    /// 字符串转成对应的编码
    /// </summary>
    /// <param name="str"></param>
    /// <param name="dataEncoding"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private byte[] ConvertStringToBytes(string str, EnumDataEncoding dataEncoding)
    {
        byte[] bytes;
        switch (dataEncoding)
        {
            case EnumDataEncoding.UTF8:
                bytes= Encoding.UTF8.GetBytes(str);
                break;
            case EnumDataEncoding.UTF32:
                bytes = Encoding.UTF32.GetBytes(str);
                break;
            case EnumDataEncoding.ASCII:
                bytes = Encoding.ASCII.GetBytes(str);
                break;
            case EnumDataEncoding.Unicode:
                bytes = Encoding.Unicode.GetBytes(str);
                break;
            default: throw new Exception("找不到对应的字符串编码枚举的处理");
        }
        return bytes;
    }
    /// <summary>
    /// bytes转成对应的数组
    /// </summary>
    /// <param name="bytes"></param>
    /// <param name="dataEncoding"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private string ConvertBytesToString(byte[] bytes, EnumDataEncoding dataEncoding)
    {
        var str = string.Empty;
        switch (dataEncoding)
        {
            case EnumDataEncoding.UTF8:
                str = Encoding.UTF8.GetString(bytes);
                break;
            case EnumDataEncoding.UTF32:
                str = Encoding.UTF32.GetString(bytes);
                break;
            case EnumDataEncoding.ASCII:
                str = Encoding.ASCII.GetString(bytes);
                break;
            case EnumDataEncoding.Unicode:
                str = Encoding.Unicode.GetString(bytes);
                break;
            default: throw new Exception("找不到对应的字符串编码枚举的处理");
        }
        return str;
    }
    /// <summary>
    /// 把数据编码转换成.net 的encoding对象
    /// </summary>
    /// <param name="dataEncoding"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private Encoding ConvertEnumDataEncodingToEncoding(EnumDataEncoding dataEncoding)
    {
        Encoding encoding;
        switch (dataEncoding)
        {
            case EnumDataEncoding.ASCII:
                encoding = Encoding.ASCII;
                break;
            case EnumDataEncoding.UTF32:
                encoding = Encoding.UTF32;
                break;
            case EnumDataEncoding.UTF8:
                encoding = Encoding.UTF8;
                break;
            case EnumDataEncoding.Unicode:
                encoding = Encoding.Unicode;
                break;
            default: throw new Exception("找不到对应的数据编码的枚举的处理");
        }

        return encoding;
    }
    /// <summary>
    /// 写字符串的特殊处理
    /// </summary>
    /// <param name="client"></param>
    /// <param name="address"></param>
    /// <param name="value"></param>
    /// <param name="dataEncoding"></param>
    /// <param name="length"></param>
    /// <param name="protocol"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private OperateResult WriteString(IReadWriteNet client,string address,string value ,EnumDataEncoding dataEncoding = 0, int length = 0, EnumProtocol protocol = 0)
    {
        OperateResult result;
        switch (protocol)
        {
            case EnumProtocol.三菱MCASCII:
                var ascii = client as MelsecMcAsciiNet;
                 result= ascii.Write(address, value, length, ConvertEnumDataEncodingToEncoding(dataEncoding));
                break;
            case EnumProtocol.三菱MCBinary:
                var binary = client as MelsecMcNet;
                result =  binary.Write(address, value, length, ConvertEnumDataEncodingToEncoding(dataEncoding));
                break;
            case EnumProtocol.欧姆龙FinsUdp:
                var findUdp = client as OmronFinsUdp;
                result = findUdp.Write(address, value, length);
                break;
            case EnumProtocol.欧姆龙EtherNet:
                var etherNet = client as OmronCipNet;
                result = etherNet.Write(address, value, ConvertEnumDataEncodingToEncoding(dataEncoding));
                break;
            default: throw new Exception("找不到对应的通讯协议的枚举处理");
        }
        return result;
    }

    /// <summary>
    /// 校验读取结果
    /// </summary>
    /// <param name="content"></param>
    /// <param name="success"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private string CheckReadResult(string content, bool success)
    {
        if (success)
        {
            return  content.Replace("\r","");
        }

        throw new Exception("数据读取错误");
    }
    /// <summary>
    /// 写数据
    /// </summary>
    /// <param name="client"></param>
    /// <param name="address"></param>
    /// <param name="value"></param>
    /// <param name="dataType"></param>
    /// <param name="dataEncoding"></param>
    /// <param name="length"></param>
    /// <param name="protocol"></param>
    /// <exception cref="Exception"></exception>
    public void WriteData(IReadWriteNet client,string address,string value ,EnumDataType dataType,EnumDataEncoding dataEncoding=0,int length = 0,EnumProtocol protocol=0)
    {
        ArgumentNullException.ThrowIfNull(client);
        ArgumentException.ThrowIfNullOrEmpty(address);
        OperateResult result;
        switch (dataType)
        {
            case EnumDataType.字符串:
                // 直接写原始二进制数据 关于字符串
                //var bytes = ConvertStringToBytes(value, dataEncoding);
                //result = client.Write(address, bytes);
                // 写字符串  这边要特殊处理一下
                result = WriteString(client, address, value, dataEncoding, length, protocol);
                break;
            case EnumDataType.布尔值:
                result = client.Write(address, Convert.ToBoolean(value));
                break;
            case EnumDataType.整数16位:
                result = client.Write(address, Convert.ToInt16(value));
                break;
            case EnumDataType.整数32位:
                result = client.Write(address, Convert.ToInt32(value));
                break;
            case EnumDataType.整数64位:
                result = client.Write(address, Convert.ToInt64(value));
                break;
            case EnumDataType.无符号数16位:
                result = client.Write(address, Convert.ToUInt16(value));
                break;
            case EnumDataType.无符号数32位:
                result = client.Write(address, Convert.ToUInt32(value));
                break;
            case EnumDataType.无符号数64位:
                result = client.Write(address, Convert.ToUInt64(value));
                break;
            case EnumDataType.短浮点:
                result = client.Write(address, float.Parse(value));
                break;
            case EnumDataType.长浮点:
                result = client.Write(address, Convert.ToDouble(value));
                break;
            default: throw new Exception("没有对应的枚举的变量类型处理");
        }

        if (result==null || !result.IsSuccess)
        {
            throw new Exception("写入参数错误");
        }
    }

    /// <summary>
    /// 读数据
    /// </summary>
    /// <param name="client"></param>
    /// <param name="address"></param>
    /// <param name="dataType"></param>
    /// <param name="dataEncoding"></param>
    /// <param name="length"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public string ReadData(IReadWriteNet client, string address, EnumDataType dataType,EnumDataEncoding dataEncoding=0,int length=0 )
    {
        ArgumentNullException.ThrowIfNull(client);
        ArgumentException.ThrowIfNullOrEmpty(address);
        var value = string.Empty;
        switch (dataType)
        {
            case EnumDataType.字符串:
                // var strResult = client.ReadString(address,(ushort)length,ConvertEnumDataEncodingToEncoding(dataEncoding)); 后面的版本可以传指定的编码进来  看看读取的时候  是都可以读出来  不然就要根据读出来的淄川手动转换
                var strResult = client.ReadString(address, (ushort)length);
                value = CheckReadResult(strResult.Content, strResult.IsSuccess);
                break;
            case EnumDataType.布尔值:
                var boolResult = client.ReadBool(address);
                value = CheckReadResult(boolResult.Content.ToString(), boolResult.IsSuccess);
                break;
            case EnumDataType.整数16位:
                var int16Result = client.ReadInt16(address);
                value = CheckReadResult(int16Result.Content.ToString(), int16Result.IsSuccess);
                break;
            case EnumDataType.整数32位:
                var int32Result = client.ReadInt32(address);
                value = CheckReadResult(int32Result.Content.ToString(), int32Result.IsSuccess);
                break;
            case EnumDataType.整数64位:
                var int64Result = client.ReadInt64(address);
                value = CheckReadResult(int64Result.Content.ToString(), int64Result.IsSuccess);
                break;
            case EnumDataType.无符号数16位:
                var uint16Result = client.ReadUInt16(address);
                value = CheckReadResult(uint16Result.Content.ToString(), uint16Result.IsSuccess);
                break;
            case EnumDataType.无符号数32位:
                var uint32Result = client.ReadUInt32(address);
                value = CheckReadResult(uint32Result.Content.ToString(), uint32Result.IsSuccess);
                break;
            case EnumDataType.无符号数64位:
                var uint64Result = client.ReadUInt64(address);
                value = CheckReadResult(uint64Result.Content.ToString(), uint64Result.IsSuccess);
                break;
            case EnumDataType.短浮点:
                var floatResult = client.ReadFloat(address);
                value = CheckReadResult(floatResult.Content.ToString(), floatResult.IsSuccess);
                break;
            case EnumDataType.长浮点:
                var doubleResult = client.ReadDouble(address);
                value = CheckReadResult(doubleResult.Content.ToString(), doubleResult.IsSuccess);
                break;
            default: throw new Exception("没有对应的枚举的变量类型处理");
        }

        return value;
    }
}